#include <leveldb/db.h>
#include <leveldb/comparator.h>
#include <leveldb/write_batch.h>
#include <leveldb/filter_policy.h>
#include <leveldb/cache.h>
#include <iostream>

int main()
{
    // 创建仓库
    leveldb::DB *db;
    leveldb::Options options;
    options.create_if_missing = true;
    leveldb::Status status = leveldb::DB::Open(options, "./test_db", &db);
    if (!status.ok())
    {
        std::cerr << status.ToString() << std::endl;
        return -1;
    }
    // put、get、delete操作
    std::string key1 = "key1", value1 = "value1", value;
    leveldb::Status s = db->Put(leveldb::WriteOptions(), key1, value1);
    if (s.ok())
    {
        s = db->Get(leveldb::ReadOptions(), key1, &value);
        std::cout << value << std::endl;
    }
    if (s.ok())
    {
        s = db->Delete(leveldb::WriteOptions(), key1);
    }

    // 原子更新
    std::cout << "-------------------Atomic update----------------------" << std::endl;
    std::string atomicKey1 = "atomic1";
    std::string atomicKey2 = "atomic2";
    leveldb::WriteOptions atomicWriteOpt;
    atomicWriteOpt.sync = true; // 开启同步写，速度会比异步写慢很多
    leveldb::Status atomicStatus;
    leveldb::WriteBatch batch;
    batch.Put(atomicKey1, "atomic value1");
    batch.Put(atomicKey2, "atomic value2");
    atomicStatus = db->Write(atomicWriteOpt, &batch);
    std::string atomicVal1, atomicVal2;
    if (atomicStatus.ok())
    {
        atomicStatus = db->Get(leveldb::ReadOptions(), atomicKey1, &atomicVal1);
        if (atomicStatus.ok())
        {
            std::cout << "atomicVal1: " << atomicVal1 << std::endl;
        }
        atomicStatus = db->Get(leveldb::ReadOptions(), atomicKey2, &atomicVal2);
        if (atomicStatus.ok())
        {
            std::cout << "atomicVal2: " << atomicVal2 << std::endl;
        }
    }

    // 迭代器
    std::cout << "-------------------Iterator----------------------" << std::endl;
    leveldb::Iterator *it = db->NewIterator(leveldb::ReadOptions());
    // 正向遍历
    for (it->SeekToFirst(); it->Valid(); it->Next())
    {
        std::cout << it->key().ToString() << ":" << it->value().ToString() << std::endl;
    }
    assert(it->status().ok());
    // 反向遍历
    for (it->SeekToLast(); it->Valid(); it->Prev()) // 速度比正向遍历慢些
    {
        std::cout << it->key().ToString() << ":" << it->value().ToString() << std::endl;
    }
    assert(it->status().ok());
    // 遍历指定范围[key1,key2)
    for (it->Seek(atomicKey1); it->Valid() && it->key().ToString() < atomicKey2; it->Next())
    {
        std::cout << it->key().ToString() << ":" << it->value().ToString() << std::endl;
    }
    assert(it->status().ok());
    // 释放new出来的迭代器
    delete it;

    // Snapshot 快照
    std::cout << "---------------Snapshot---------------" << std::endl;
    leveldb::ReadOptions snapReadOpt;
    snapReadOpt.snapshot = db->GetSnapshot();
    leveldb::WriteOptions snapWriteOpt;
    snapWriteOpt.sync = true;
    leveldb::Status snapStatus = db->Put(snapWriteOpt, "snapkey", "snapshot value");
    assert(snapStatus.ok());
    leveldb::Iterator *snapIt = db->NewIterator(snapReadOpt);
    // 遍历snap内的 key:value
    for (snapIt->SeekToFirst(); snapIt->Valid(); snapIt->Next())
    {
        std::cout << snapIt->key().ToString() << ":" << snapIt->value().ToString() << std::endl;
    }
    assert(snapIt->status().ok());
    delete snapIt;
    std::string snapValue;
    snapStatus = db->Get(leveldb::ReadOptions(), "snapkey", &snapValue);
    assert(snapStatus.ok());
    std::cout << "snapValue: " << snapValue << std::endl;

    // slice 内部没有string对象，只是包含了一个指向string对象的指针，见成员变量
    std::cout << "---------------Slice---------------" << std::endl;
    leveldb::Slice slice("slice slice slice");
    slice.remove_prefix(strlen("slice ")); // 移除n个前缀字符
    if (slice.starts_with("slice"))
    {
        std::cout << slice.ToString() << " start with slice" << std::endl;
    }
    // 关闭仓库
    delete db;

    // Comparator
    std::cout << "-------------Comparator---------------" << std::endl;
    class CustomCmp : public leveldb::Comparator
    {
    public:
        // 从大到小排序排序: 1、判断字典序 2、字典序相同判断字符串长度
        virtual int Compare(const leveldb::Slice &a, const leveldb::Slice &b) const
        {
            std::size_t size = a.size() < b.size() ? a.size() : b.size();
            for (auto i = 0; i < size; ++i)
            {
                if (a[i] < b[i])
                {
                    return 1;
                }
                else if (a[i] > b[i])
                {
                    return -1;
                }
            }
            if (a.size() < b.size())
                return 1;
            else if (a.size() > b.size())
                return -1;
            return 0;
        }

        virtual const char *Name() const
        {
            return "CustomCmp";
        }
        virtual void FindShortestSeparator(std::string *start, const leveldb::Slice &limit) const {}
        virtual void FindShortSuccessor(std::string *key) const {}
    };
    leveldb::Options cmpOpt;
    CustomCmp customCmp;
    cmpOpt.comparator = &customCmp;
    cmpOpt.create_if_missing = true;
    leveldb::DB *cmpdb = nullptr;
    leveldb::Status cmpStatus = leveldb::DB::Open(cmpOpt, "./cmp_db", &cmpdb);
    if (!cmpStatus.ok())
    {
        std::cout << cmpStatus.ToString() << std::endl;
        assert(cmpStatus.ok());
        return -1;
    }
    leveldb::WriteBatch cmpBatch;
    cmpBatch.Put("cmp1", "value1");
    cmpBatch.Put("cmp100", "value2");
    cmpBatch.Put("cmp2", "value3");
    cmpBatch.Put("cmp200", "value4");
    cmpStatus = cmpdb->Write(leveldb::WriteOptions(), &cmpBatch);
    if (!cmpStatus.ok())
    {
        std::cout << cmpStatus.ToString() << std::endl;
        delete cmpdb;
        return -1;
    }
    leveldb::Iterator *cmpIt = cmpdb->NewIterator(leveldb::ReadOptions());
    for (cmpIt->SeekToFirst(); cmpIt->Valid(); cmpIt->Next())
    {
        std::cout << cmpIt->key().ToString() << ":" << cmpIt->value().ToString() << std::endl;
    }
    assert(cmpIt->status().ok());
    delete cmpIt;
    delete cmpdb;

    // Performance 调优 1、block size 块大小 2、compression 压缩 3、cache 缓存 4、键(key)布局 5、布隆(bloom)过滤器
    std::cout << "-------------Performance---------------" << std::endl;
    leveldb::Options perforOpt;
    perforOpt.create_if_missing = true;
    perforOpt.block_size = 4096;                                    // 8K 块大小
    perforOpt.compression = leveldb::kSnappyCompression;            // snappy 压缩
    perforOpt.block_cache = leveldb::NewLRUCache(1024 * 1024 * 16); // 16MB 缓存
    perforOpt.filter_policy = leveldb::NewBloomFilterPolicy(10);    // bloom过滤器
    /*
        // 如果使用自定义比较器，需要确保过滤策略和比较器兼容。
        // 例如比较键时忽略尾随空格的比较器，不能使用NewBloomFilterPolicy，而需要提供忽略空格的自定义过滤策略
    */
    leveldb::DB *perforDb;
    leveldb::Status perforStatus = leveldb::DB::Open(perforOpt, "./performance_db", &perforDb);
    if (!perforStatus.ok())
    {
        std::cout << perforStatus.ToString() << std::endl;
        assert(perforStatus.ok());
        return -1;
    }
    leveldb::WriteBatch perforBatch;
    perforBatch.Put("perfor1", "value1");
    perforBatch.Put("perfor100", "value2");
    perforBatch.Put("perfor2", "value3");
    perforBatch.Put("perfor200", "value4");
    perforStatus = perforDb->Write(leveldb::WriteOptions(), &perforBatch);
    if (!perforStatus.ok())
    {
        std::cout << cmpStatus.ToString() << std::endl;
        delete cmpdb;
        return -1;
    }
    // 迭代时，可能不想缓存中的数据被破坏，可以设置ReadOptions禁用缓存
    leveldb::ReadOptions perforReadOpt;
    perforReadOpt.fill_cache = false;
    leveldb::Iterator *perforIt = perforDb->NewIterator(perforReadOpt);
    for (perforIt->SeekToFirst(); perforIt->Valid(); perforIt->Next())
    {
        std::cout << perforIt->key().ToString() << ":" << perforIt->value().ToString() << std::endl;
    }
    assert(perforIt->status().ok());
    delete perforOpt.filter_policy;
    delete perforOpt.block_cache;
    delete perforIt;
    delete perforDb;

    // Checksums 校验和
    std::cout << "-------------Checksums---------------" << std::endl;
    /*
    leveldb 将校验和与它存储在文件系统中的所有数据进行关联，对于这些校验和，有两个独立的控制：
        ReadOptions::verify_checksums 可以设置为 true，以强制对所有从文件系统读取的数据进行校验。
            默认为 false，即，不会进行这样的校验。
        Options::paranoid_checks 在数据库打开之前设置为 true ，以使得数据库一旦检测到数据损毁立即报错。根据数据库损坏的部位，报错可能是在打开数据库后，也可能是在后续执行某个操作的时候。
            该配置默认是关闭状态，即，持久化存储部分损坏数据库也能继续使用。
        如果数据库损坏了(当开启 Options::paranoid_checks 的时候可能就打不开了)，leveldb::RepairDB() 函数可以用于对尽可能多的数据进行修复。
    */

    // Approximate Sizes 近似空间大小
    std::cout << "-------------Approximate Sizes---------------" << std::endl;
    // GetApproximateSizes获取一个或多个键区间占据的文件系统近似大小
    leveldb::Options getSizeOpt;
    getSizeOpt.create_if_missing = true;
    leveldb::DB *getSizeDB;
    leveldb::Status getSizeStatus = leveldb::DB::Open(getSizeOpt, "get_size_db", &getSizeDB);
    if (!getSizeStatus.ok())
    {
        std::cout << getSizeStatus.ToString() << std::endl;
        assert(getSizeStatus.ok());
        return -1;
    }
    leveldb::WriteBatch getSizeBatch;
    leveldb::WriteOptions getSizeWriteOpt;
    getSizeWriteOpt.sync = true;
    for (char i = 'a'; i <= 'z'; ++i)
    {
        std::string key = std::string(1, i), value = "value" + std::to_string(i);
        getSizeBatch.Put(key, value);
    }
    getSizeStatus = getSizeDB->Write(getSizeWriteOpt, &getSizeBatch);
    if (!getSizeStatus.ok())
    {
        std::cout << getSizeStatus.ToString() << std::endl;
        assert(getSizeStatus.ok());
        return -1;
    }
    // leveldb::Slice start = "a", end = "{";
    getSizeDB->CompactRange(nullptr, nullptr); // 手动落盘
    std::string size1Val, size2Val;
    // getSizeDB->Get(leveldb::ReadOptions(), "size0", &size1Val);
    // getSizeDB->Get(leveldb::ReadOptions(), "size99", &size2Val);
    // std::cout << "size1Val: " << size1Val << " size2Val: " << size2Val << std::endl;
    leveldb::Range range[2];
    range[0] = leveldb::Range("a", "{"); // 注意，从字典序来说，size2 > size100。获取的似乎也不是很准确，比如 Range("a","z") 返回0
    range[1] = leveldb::Range("k", "{");
    uint64_t size[2];
    getSizeDB->GetApproximateSizes(range, 2, size);
    std::cout << "range[0] size: " << size[0] << " range[1] size: " << size[1] << std::endl;
    auto getSizeIt = getSizeDB->NewIterator(leveldb::ReadOptions());
    for (getSizeIt->SeekToFirst(); getSizeIt->Valid(); getSizeIt->Next())
    {
        std::cout << getSizeIt->key().ToString() << ":" << getSizeIt->value().ToString() << std::endl;
    }
    delete getSizeIt;
    delete getSizeDB;

    // Environment 环境变量
    std::cout << "-------------Environment---------------" << std::endl;
    /*
    由 leveldb 发起的全部文件操作以及其它的操作系统调用最后都会被路由给一个 leveldb::Env 对象。
        用户也可以提供自己的 Env 实现以达到更好的控制。
        比如，如果应用程序想要针对 leveldb 的文件 IO 引入一个人工延迟以限制 leveldb 对同一个系统中其它应用的影响：

        // 定制自己的 Env
        class SlowEnv : public leveldb::Env {
        ... implementation of the Env interface ...
        };

        SlowEnv env;
        leveldb::Options options;
        // 用定制的 Env 打开数据库
        options.env = &env;
        Status s = leveldb::DB::Open(options, ...);
     */

    // Portable 可移植性
    /*
    如果某个特定平台提供 leveldb/port/port.h 导出的类型/方法/函数实现，那么 leveldb 可以被移植到该平台上，
        更多细节见 leveldb/port/port_example.h。
    另外，新平台可能还需要一个新的默认的 leveldb::Env 实现。具体可参考 leveldb/util/env_posix.h 实现。
    */
    return 0;
}
